from backend import *
from copy import deepcopy
from easydict import EasyDict

class Player:
    def __init__(self, blocks, level, weapon, relics_group):
        self.blocks=blocks
        self.level=level
        self.blocks_raw=deepcopy(blocks)
        self.booster=Booster()
        self.weapon=weapon
        self.relics_group=relics_group

        self.act_delays={}
        self.elem_attach=EasyDict({'type':0, 'amount':0, 'time':elem_keep_time, 'last_time':0, 'counter':0, 'timer':-10})
        self.atk_elem={'normal':[0,0], 'thump':[0,0], 'skill':[0,0], 'burst':[0,0]} # type, amount


    def reset(self):
        self.blocks = deepcopy(self.blocks_raw)
        self.booster = Booster()
        self.booster.boost_relics(self.blocks, self.relics_group)
        self.booster.boost_weapon(self.blocks, self.weapon)

    def get_act_delay(self, act):
        return self.act_delays[act]

    def get_attached_element_type(self):
        return self.elem_attach.type

    def trig(self, trig_type, team):
        if trig_type in self.booster.triggers:
            for trigger in self.booster.triggers[trig_type]:
                team.add_op(action='op.data.player.backend.boost_ex(op.data.player.blocks, op.data.act)', delay=0, post='',time_stamp=team.tick,
                            ex_data={'player':self, 'act':{'script':trigger.start}})
                team.add_op(action='op.data.player.backend.boost_ex(op.data.player.blocks, op.data.act)', delay=trigger.duration, post='', time_stamp=team.tick,
                            ex_data={'player': self, 'act': {'script': trigger.end}})

    # TODO: 获取元素反应加成
    def get_react_block(self, enemy, elem_type, elem_amount, tick):
        react_type, react_mode=enemy.attach_element(elem_type, elem_amount, tick)
        if react_mode=='boost':
            base_rate=get_react_boost(enemy.get_attached_element_type(), elem_type, react_type)
            master_boost=(2.78*self.blocks.damage.elem.master)/(self.blocks.damage.elem.master+1400)
            res=base_rate*(1+master_boost+self.blocks.damage.elem.boost[react_type])
            return react_mode, res
        if react_mode=='fusion':
            level_dmg=get_react_level_rate(self.level)
            base_rate = get_react_boost(enemy.get_attached_element_type(), elem_type, react_type)
            master_boost = (16 * self.blocks.damage.elem.master) / (self.blocks.damage.elem.master + 2000)
            res = level_dmg * base_rate * (1 + master_boost + self.blocks.damage.elem.boost[react_type])
            return react_mode, (res, )

    #元素附着量计算
    def attach_element(self, elem_type, elem_amount, tick):
        def reset_elem():
            self.elem_attach.type = elem_types['phy']
            self.elem_attach.amount = 0
            self.elem_attach.timer = -10
            self.elem_attach.counter = 0

        #计算元素衰减
        if self.elem_attach.type != elem_types['phy']:  # 有元素附着
            rest_amount, rest_time=reduce_elem(self.elem_attach.amount, self.elem_attach.time, tick-self.elem_attach.last_time)
            if rest_amount<=0 and rest_time<=0:
                reset_elem()

        if elem_type == elem_types['phy']:
            return

        if self.elem_attach.type==elem_types['phy']: #无元素附着
            self.elem_attach.type=elem_type
            self.elem_attach.amount=elem_amount
            self.elem_attach.time=elem_keep_time
            self.elem_attach.last_time=tick
            self.elem_attach.timer=tick
            self.elem_attach.counter=elem_attach_count
        else:
            self.elem_attach.counter-=1

            if self.elem_attach.counter<=0:
                self.elem_attach.counter = elem_attach_count
            elif tick-self.elem_attach.timer>=elem_attach_interval:
                self.elem_attach.timer = tick
                self.elem_attach.counter = elem_attach_count
            else:
                return

            if self.elem_attach.type==elem_type: #与已附着元素类型相同
                self.elem_attach.amount=max(self.elem_attach.amount, elem_amount) #元素量取最高
                self.elem_attach.time=elem_keep_time #刷新保留时间
            else:
                react_type=get_react_type(self.elem_attach.type, elem_type)

                cost_elem_rate=get_react_cost(self.elem_attach.type, elem_type)
                self.elem_attach.amount-=elem_amount*cost_elem_rate
                if self.elem_attach.amount<=0:
                    reset_elem()

                if react_type in boost_react:
                    return react_type, 'boost'
                elif react_type in fusion_react:
                    return react_type, 'fusion'
                elif react_type==react_types['冻结']:
                    return react_type, 'freeze'
                else:
                    return react_type, 'other'
        return None, 'other'

    def cal_damage(self, enemy, action, team):
        elem_type, elem_amount=self.atk_elem[action]

        atk_blk=self.blocks.damage.atk.base*(1+self.blocks.damage.atk.rate)+self.blocks.damage.atk.const
        rate_blk=self.blocks.damage.rate.skill[action]*(1+self.blocks.damage.rate.ex)
        boost_blk=1+self.blocks.damage.boost.base+self.blocks.damage.boost.elem[elem_type]+self.blocks.damage.boost.act[action]
        crit_blk=1+self.blocks.damage.crit.rate*self.blocks.damage.crit.atk
        react_mode, elem_blk=self.get_react_block(enemy, elem_type, elem_amount, team.tick)

        damage=atk_blk*rate_blk*boost_blk*crit_blk
        if react_mode=='boost':
            damage*=elem_blk
        elif react_mode=='fusion':
            damage+=enemy.defense_damage(elem_blk, )


    def attack(self, enemy, action, team):
        #boost
        blk=deepcopy(self.blocks)
        self.booster.boost_dynamic(blk, self, enemy)

        #attack


        self.trig(action, team)

    def defense_damage(self, damage, damage_elem, attacker_level):
        def_rate=self.blocks.prop.defense.base*self.blocks.prop.defense.rate+self.blocks.prop.defense.const
        damage_rate=(attacker_level+100)/((attacker_level+100)+(1-def_rate)*(self.level+100))
        resist=self.blocks.prop.defense.elem[damage_elem]
        if resist>75:
            resist_rate=1/(1+4*resist)
        elif resist>=0:
            resist_rate=1-resist
        else:
            resist_rate=1-resist/2
        return damage*damage_rate*resist_rate